/**
 * 对cfifo.h的测试。
 */
#include <stdio.h>
#include <stdlib.h>

#include "test_tools.h"

static void cfifo_create_test(void)
{
    size_t avail_size_set[] =   \
        {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384};
    size_t avail_size_set_len = sizeof(avail_size_set) / sizeof(size_t);
    size_t invalid_size_set[] = {
        269, 449, 950, 1139, 1587, 2615, 2788, 2921,
        3309, 3707, 4217, 4397, 4506, 5919, 6211, 6341,
        6566, 7654, 8152, 0, 1, 11151, 11855, 12298,
        13514, 13832, 14100, 14559, 14741, 15035, 15320, 16110
    };
    size_t invalid_size_set_len = sizeof(invalid_size_set) / sizeof(size_t);
    size_t real_size = 0;
    int cnt = 0;
    int ret = 0;

    /// 测试创建有效长度的cfifo。
    for (cnt = 0; cnt < avail_size_set_len; cnt++) {
        ret = 0;
        struct cfifo *fifo = CREATE_CFIFO_WITH_PADDING(avail_size_set[cnt]);
        if (fifo == NULL) {
            real_size = 0;
            break;
        }
        real_size = cfifo_size(fifo);
        ret = CFIFO_FULLY_TEST(fifo);
        if (ret != 0) {
            DESTROY_CFIFO_WITH_PADDING(fifo);
            break;
        }
        if (real_size != avail_size_set[cnt]) {
            DESTROY_CFIFO_WITH_PADDING(fifo);
            break;
        }
        DESTROY_CFIFO_WITH_PADDING(fifo);
    }
    if (cnt != avail_size_set_len) {
        LOG_TEST_FAIL("failed to create the (%d)th cfifo!\n", cnt);
        printf("expect size: %zu, real size: %zu\n", 
                avail_size_set[cnt], 
                real_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("all cfifo are created successfully!\n");
    }

    /// 测试创建无效长度的cfifo。
    for (cnt = 0; cnt < invalid_size_set_len; cnt++) {
        struct cfifo *fifo = CREATE_CFIFO_WITH_PADDING(invalid_size_set[cnt]);
        if (fifo != NULL) {
            DESTROY_CFIFO_WITH_PADDING(fifo);
            break;
        }
    }
    if (cnt != invalid_size_set_len) {
        LOG_TEST_FAIL("ont invalid cfifo was created! cnt: %d, size: %zu\n", cnt, invalid_size_set[cnt]);
    } else {
        LOG_TEST_PASS("no invalid cfifo was created!\n");
    }
}

/// 填充入队的buffer
static void __mk_buffer(void *buffer, unsigned char begin, size_t size)
{
    unsigned char *str = buffer;
    for (int i = 0; i < size; i++) {
        str[i] = begin;
        begin++;
    }
}

/// 测试出队的buffer
static int __test_buffer(void *buffer, unsigned char begin, size_t size)
{
    unsigned char *str = buffer;
    unsigned char current = begin;
    int cnt = 0;
    while (cnt < size) {
        if (str[cnt] != current) {
            break;
        }
        cnt++;
        current++;
    }

    return cnt;
}

/// 对比两个buffer
static int __cmp_buffers(void *buffer1, void *buffer2, size_t size)
{
    int cnt = 0;
    unsigned char *str1 = buffer1;
    unsigned char *str2 = buffer2;
    while (cnt < size) {
        if (str1[cnt] != str2[cnt]) {
            break;
        }
        cnt++;
    }
    return cnt;
}

static void cfifo_basic_test(void)
{
    unsigned char write_count = 0;
    unsigned char read_point = 8;
    size_t fifo_size = 64;
    size_t buffer_size = 256;
    size_t cnt = 0;
    size_t r_cnt = 0;
    size_t this_size = 0;
    size_t real_size = 0;
    size_t matched_size = 0;
    size_t read_size = 0;
    int ret = 0;
    void *buffer = malloc(buffer_size);
    void *compare_buffer = malloc(buffer_size);
    /// 边界对齐的进行一比一读写的数据大小。
    size_t aligned_write_sizes_array[] = {
        8, 8, 8, 8, 8, 8, 8, 8,
        16, 16, 16, 16,
        32, 32,
        64
    };
    size_t aligned_write_sizes_len =    \
        sizeof(aligned_write_sizes_array) / sizeof(size_t);
    struct cfifo *fifo = CREATE_CFIFO_WITH_PADDING(fifo_size);
    
    if (fifo == NULL) {
        printf("failed to create fifo!\n");
    }
    
    ret = CFIFO_FULLY_TEST(fifo);
    if (ret != 0) {
        SHOW_CFIFO_ERROR_INFO(ret);
    }

    /// 一读一写
    for (cnt = 0; cnt < aligned_write_sizes_len; cnt++) {
        this_size = aligned_write_sizes_array[cnt];
        matched_size = 0;
        read_size = 0;
        ret = 0;
        __mk_buffer(buffer, 0, this_size);
        real_size = cfifo_in(fifo, buffer, this_size);
        if (real_size != this_size) {
            ret = CFIFO_FULLY_TEST(fifo);
            break;
        }
        read_size = cfifo_out(fifo, compare_buffer, this_size);
        if (read_size != this_size) {
            ret = CFIFO_FULLY_TEST(fifo);
            break;
        }
        matched_size = __test_buffer(compare_buffer, 0, this_size);
        if (matched_size != this_size) {
            ret = CFIFO_FULLY_TEST(fifo);
            break;
        }
        ret = CFIFO_FULLY_TEST(fifo);
        if (ret != 0) {
            break;
        }
    }
    if (cnt != aligned_write_sizes_len) {
        LOG_TEST_FAIL("cnt: %zu\n", cnt);
        printf("size: %zu, real_size: %zu, matched: %zu, read: %zu\n", 
            aligned_write_sizes_array[cnt], 
            real_size, 
            matched_size, 
            read_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("pass!\n");
    }

    /// 写满再读
    for (cnt = 0, r_cnt = 0; cnt < aligned_write_sizes_len; cnt++) {
        this_size = aligned_write_sizes_array[cnt];
        matched_size = 0;
        read_size = 0;
        ret = 0;
        
        int flag = 0;
        if (this_size >= read_point) {
            while (r_cnt < cnt) {
                matched_size = 0;
                read_size = 0;
                ret = 0;
                read_size = cfifo_out(fifo, compare_buffer, this_size / 2);
                if (read_size != this_size / 2) {
                    ret = CFIFO_FULLY_TEST(fifo);
                    flag = 1;
                    break;
                }
                matched_size = __test_buffer(compare_buffer, 0, this_size / 2);
                if (matched_size != this_size / 2) {
                    ret = CFIFO_FULLY_TEST(fifo);
                    flag = 1;
                    break;
                }
                ret = CFIFO_FULLY_TEST(fifo);
                if (ret != 0) {
                    flag = 1;
                    break;
                }

                r_cnt++;
            }
            read_point *= 2;
        }
        if (flag) {
            break;
        }

        __mk_buffer(buffer, 0, this_size);
        real_size = cfifo_in(fifo, buffer, this_size);
        if (real_size != this_size) {
            ret = CFIFO_FULLY_TEST(fifo);
            break;
        }
        ret = CFIFO_FULLY_TEST(fifo);
        if (ret != 0) {
            break;
        }
    }
    if (cnt != aligned_write_sizes_len || r_cnt + 1 != cnt) {
        LOG_TEST_FAIL("cnt: %zu, r_cnt: %zu, read_point: %uhhd\n", cnt, r_cnt, read_point);
        printf("size: %zu, real_size: %zu, matched: %zu, read: %zu\n", 
            aligned_write_sizes_array[cnt], 
            real_size, 
            matched_size, 
            read_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("pass!\n");
    }

    /// 缓存满时的入队测试。
    __mk_buffer(buffer, 0, 13);
    real_size = cfifo_in(fifo, buffer, 13);
    ret = CFIFO_FULLY_TEST(fifo);
    if (real_size != 0 || ret != 0) {
        LOG_TEST_FAIL("wrote data into a full fifo? write_count: %zu\n", real_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("full cfifo test successful!\n");
    }

    /// 入队数据大于空闲缓存的测试。
    real_size = cfifo_out(fifo, buffer, 13);
    ret = CFIFO_FULLY_TEST(fifo);
    if (real_size != 13 || ret != 0) {
        LOG_TEST_FAIL("read_size: %zu\n", real_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("\n");
    }
    if (__test_buffer(buffer, 0, 13) != 13) {
        LOG_TEST_FAIL("data get from fifo is not fully matched!\n");
    } else {
        LOG_TEST_PASS("data get from fifo is all matched!\n");
    }

    __mk_buffer(buffer, 0, 16);
    real_size = cfifo_in(fifo, buffer, 16);
    ret = CFIFO_FULLY_TEST(fifo);
    if (real_size != 13 || ret != 0) {
        LOG_TEST_FAIL("write_size: %zu\n", real_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("\n");
    }

    /// 出队大于现有缓冲大小的测试。
    real_size = cfifo_out(fifo, buffer, fifo_size - 13);
    ret = CFIFO_FULLY_TEST(fifo);
    if (real_size != fifo_size - 13 || ret != 0) {
        LOG_TEST_FAIL("read_size: %zu\n", real_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("\n");
    }
    if (__test_buffer(buffer, 13, fifo_size - 13) != fifo_size - 13) {
        LOG_TEST_FAIL("data get from fifo is not fully matched!\n");
    } else {
        LOG_TEST_PASS("data get from fifo is all matched!\n");
    }

    real_size = cfifo_out(fifo, buffer, 16);
    ret = CFIFO_FULLY_TEST(fifo);
    if (real_size != 13 || ret != 0) {
        LOG_TEST_FAIL("read_size: %zu\n", real_size);
        SHOW_CFIFO_ERROR_INFO(ret);
    } else {
        LOG_TEST_PASS("\n");
    }
    if (__test_buffer(buffer, 0, 13) != 13) {
        LOG_TEST_FAIL("data get from fifo is not fully matched!\n");
    } else {
        LOG_TEST_PASS("data get from fifo is all matched!\n");
    }

    DESTROY_CFIFO_WITH_PADDING(fifo);
    free(compare_buffer);
    free(buffer);
}

static void cfifo_cross_border_test(void)
{
    size_t fifo_size = 64;
    size_t buffer_size = 256;
    size_t real_size = 0;
    size_t cnt = 0;
    void *buffer = malloc(buffer_size);
    void *compare_buffer = malloc(buffer_size);
    int ret = 0;
    struct cfifo *fifo = CREATE_CFIFO_WITH_PADDING(fifo_size);
    
    if (fifo == NULL) {
        printf("failed to create fifo!\n");
    }
    
    ret = CFIFO_FULLY_TEST(fifo);
    if (ret != 0) {
        SHOW_CFIFO_ERROR_INFO(ret);
    }

    /// 重复十六次，每次循环总共出入的数据为素数，所以每次写入测试数据的起点都不一样。
    for (cnt = 0; cnt < 16; cnt++) {
        /// 转移读写指针
        __mk_buffer(buffer, 0, 51);
        cfifo_in(fifo, buffer, 51);
        cfifo_out(fifo, buffer, 51);
        
        // 写入跨边界的连续数据
        __mk_buffer(buffer, 0, 32);
        real_size = cfifo_in(fifo, buffer, 32);
        ret = CFIFO_FULLY_TEST(fifo);
        if (real_size != 32 || ret != 0) {
            LOG_TEST_FAIL("(%zu) read_size: %zu\n", real_size, cnt);
            SHOW_CFIFO_ERROR_INFO(ret);
            break;
        }

        real_size = cfifo_out(fifo, compare_buffer, 32);
        if (real_size != 32 || ret != 0) {
            LOG_TEST_FAIL("(%zu) read_size: %zu\n", real_size, cnt);
            SHOW_CFIFO_ERROR_INFO(ret);
            break;
        }
        if (__cmp_buffers(compare_buffer, buffer, 32) != 32) {
            LOG_TEST_FAIL("(%zu) data get from fifo is not fully matched!\n", cnt);
            break;
        }
    }

    if (cnt == 16) {
        LOG_TEST_PASS("\n");
    }

    if (!cfifo_empty(fifo)) {
        LOG_TEST_FAIL("cfifo is supposed to be empty! buffer_size: %zu\n", cfifo_used_size(fifo));
    } else {
        LOG_TEST_PASS("cfifo is empty now!\n");
    }

    DESTROY_CFIFO_WITH_PADDING(fifo);
    free(compare_buffer);
    free(buffer);
}

void cfifo_tests(void)
{
    printf("\n* cfifo_test:\n");
    cfifo_create_test();
    cfifo_basic_test();
    cfifo_cross_border_test();
}
